package org.web30.nntp.service;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.NonRepeatableRequestException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import org.web30.nntp.Util;

/**
 *
 * @author Xia Zhenyu
 */
public class NNTPHttpService {

    public static final String SYNC_HEADER_NAME = "ETag";
    public static final String URL_PARAMETER_LASTSYNC_ID = "lastsyncid";
    public static final String URL_PARAMETER_GROUP_NAME = "group";
    /**
     * Http connection
     */
    private DefaultHttpClient httpConn = null;
    /**
     * Http proxy
     */
    private HttpHost httpProxy = null;
    /**
     * Http auth credential
     */
    private String authUser = null;
    /**
     * Http auth password
     */
    private String authPass = null;
    /**
     * Http NNTP Service Url
     */
    private String nntpBridgeUrl = null;
    /**
     * The last synced message id
     */
    private String lastMessageId = null;

    /**
     * Constructor
     * @param httpProxy  http proxy
     * @param httpProxyPort proxy port
     * @param url  NNTPBridge URL
     * @param user  user name of NNTPBridge
     * @param pass  password of NNTPBridge
     */
    public NNTPHttpService(String httpProxy, int httpProxyPort, String url,
            String user, String pass) {
        this.httpConn = null;
        if (httpProxy != null && httpProxyPort > 0) {
            this.httpProxy = new HttpHost(httpProxy, httpProxyPort);
        }
        this.nntpBridgeUrl = url;
        this.authUser = user;
        this.authPass = pass;
    }

    private void initConnection() {
        this.httpConn = new DefaultHttpClient();
        if (httpProxy != null) {
            this.httpConn.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
                    httpProxy);
        }
    }

    private String getErrorInfo(InputStream in) throws IOException {
        InputStreamReader reader = new InputStreamReader(in);
        char[] buf = new char[1024];
        StringBuffer bf = new StringBuffer();
        int len = 0;
        while ((len = reader.read(buf)) > 0) {
            bf.append(buf);
        }
        return bf.toString();
    }

    /**
     * Post one article to NNTPBridge
     * @param groupName  group name
     * @param in  contend to post. It's contains header of article
     * @return
     */
    public boolean postNews(String groupName, Reader in) {
        return this.postRequest(in, groupName);
    }

    /**
     * Retrieve next article of specific article Id
     * @param groupName          group name
     * @param lastArticleId      the last message id
     * @return
     */
    public BufferedReader getNewNews(String groupName, String lastArticleId) {
        ByteArrayOutputStream res = null;
        InputStreamReader in = null;
        try {
            res = this.getRequest(groupName, lastArticleId);
            if (res != null) {
                in =
                        new InputStreamReader(new ByteArrayInputStream(res.toByteArray()),
                        Util.DEFAULT_ENCODING);
            }
        } catch (Exception ex) {
            Logger.getLogger(NNTPHttpService.class.getName()).log(Level.SEVERE,
                    null, ex);
        }
        if (res == null) {
            return null;
        } else {
            return new BufferedReader(in);
        }
    }

    /**
     * Add parameter into URL
     * @param url original URL
     * @param name parameter name
     * @param value parameter value
     * @return new URL added parameters
     */
    private String addParameter(String url, String name, String value) {
        if (url != null) {
            if (value == null) {
                return url;
            }
            if (value.length() == 0) {
                return url;
            }
            if (url.indexOf("?") > 0) {
                url = url + "&" + name + "=" + value;
            } else {
                url = url + "?" + name + "=" + value;
            }
        }
        return url;
    }

    /**
     * Get the last message id which get from the last interaction with NNTPBridge
     * @return message id
     */
    public String getLastMessageId() {
        return lastMessageId;
    }

    private boolean postRequest(Reader in, String groupName) {
        ByteArrayOutputStream out = null;
        if (in == null) {
            return false;
        }
        boolean result = false;
        try {
            this.initConnection();
            String requestUrl = this.nntpBridgeUrl;
            requestUrl =
                    this.addParameter(requestUrl, NNTPHttpService.URL_PARAMETER_GROUP_NAME,
                    groupName);

            HttpPost httppost = new HttpPost(requestUrl);

            httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,
                    false);

            HttpParams params = new BasicHttpParams();
            HttpConnectionParams.setSoTimeout(params, 6000);
            HttpConnectionParams.setConnectionTimeout(params, 6000);
            ConnManagerParams.setTimeout(params, 6000);

            //httppost.setHeader("User-Agent",
            //        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)");
            httppost.setHeader("Keep-Alive", "300");
            httppost.setHeader("Proxy-Connection", "keep-alive");
            //httppost.setHeader("Connection", "keep-alive");
            httppost.setHeader("Pragma", "no-cache");
            httppost.setHeader("Cache-Control", "no-cache");

            out = new ByteArrayOutputStream();
            Util.pipe(in, out);
            ByteArrayEntity reqEntity = new ByteArrayEntity(out.toByteArray());
            reqEntity.setContentType("*/*");

            httppost.setEntity(reqEntity);
            System.out.println("executing request "
                    + httppost.getRequestLine());
            HttpResponse response = this.httpConn.execute(httppost);
            StatusLine status = response.getStatusLine();
            System.out.println(status.toString());
            if (status.getStatusCode() == 201) {
                String temp = this.getSyncHeader(response.getAllHeaders());
                if (temp != null) {
                    this.lastMessageId = temp;
                }
                result = true;
            } else {
                if (response.getEntity().isStreaming()) {
                    System.err.print(this.getErrorInfo(response.getEntity().getContent()));
                    response.getEntity().getContent().close();
                }
            }
        } catch (Exception ex) {
            if (ex instanceof NonRepeatableRequestException) {
                result = true;
            }
            Logger.getLogger(NNTPHttpService.class.getName()).log(Level.SEVERE,
                    null, ex);
        } finally {
            this.httpConn.getConnectionManager().shutdown();
        }
        return result;
    }

    private ByteArrayOutputStream getRequest(String groupName,
            String lastArticleId) {
        ByteArrayOutputStream out = null;
        String requestUrl = this.nntpBridgeUrl;
        try {
            this.initConnection();
            requestUrl =
                    this.addParameter(requestUrl, NNTPHttpService.URL_PARAMETER_GROUP_NAME,
                    groupName);
            requestUrl =
                    this.addParameter(requestUrl, NNTPHttpService.URL_PARAMETER_LASTSYNC_ID,
                    lastArticleId);
            BasicCredentialsProvider token = new BasicCredentialsProvider();
            token.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(this.authUser,
                    this.authPass));
            this.httpConn.setCredentialsProvider(token);

            HttpGet httpGet = new HttpGet(requestUrl);
            System.out.println("executing request "
                    + httpGet.getRequestLine());
            HttpResponse response = this.httpConn.execute(httpGet);
            StatusLine status = response.getStatusLine();
            System.out.println(status.toString());
            if (status.getStatusCode() == 200) {
                HttpEntity resEntity = response.getEntity();
                String temp = this.getSyncHeader(response.getAllHeaders());
                if (temp != null) {
                    this.lastMessageId = temp;
                }
                out = new ByteArrayOutputStream();
                Util.pipe(new InputStreamReader(resEntity.getContent(),
                        Util.DEFAULT_ENCODING), out);
            } else if (status.getStatusCode() == 204) {
                //No content do nothing
            } else {
                if (response.getEntity().isStreaming()) {
                    System.err.print(this.getErrorInfo(response.getEntity().getContent()));
                    response.getEntity().getContent().close();
                }
            }
        } catch (Exception ex) {
            Logger.getLogger(NNTPHttpService.class.getName()).log(Level.SEVERE,
                    null, ex);
        } finally {
            this.httpConn.getConnectionManager().shutdown();
        }
        return out;
    }

    private String getSyncHeader(Header[] headers) {
        for (int i = 0; i < headers.length; i++) {
            if (headers[i].getName().compareToIgnoreCase(NNTPHttpService.SYNC_HEADER_NAME)
                    == 0) {
                return headers[i].getValue();
            }
        }
        return null;
    }

    public static void main(String[] args) {

        HttpParams params = new BasicHttpParams();
        HttpConnectionParams.setSoTimeout(params, 6000);
        HttpConnectionParams.setConnectionTimeout(params, 6000);
        ConnManagerParams.setTimeout(params, 6000);

        DefaultHttpClient httpclient = new DefaultHttpClient();
        HttpHost proxy = new HttpHost("localhost", 8080, "http");

        httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
                proxy);

        HttpPost httppost =
                new HttpPost("http://www.web30core.org/NNTPAdapter/resources/news?group=trash");
        httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,
                false);

        //httppost.setHeader("User-Agent",
        //        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)");
        httppost.setHeader("Keep-Alive", "300");
        httppost.setHeader("Proxy-Connection", "keep-alive");
        //New add items 2 header same with poster
        httppost.setHeader("Pragma", "no-cache");
        httppost.setHeader("Cache-Control", "no-cache");
        //httppost.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        BufferedReader reader = null;

        try {
            reader =
                    new BufferedReader(new InputStreamReader(new FileInputStream(new File("test.txt")),
                    "ISO-8859-1"));
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            Util.pipe(reader, out);
            ByteArrayEntity reqEntity = new ByteArrayEntity(out.toByteArray());
            reqEntity.setContentType("*/*");
            httppost.setEntity(reqEntity);
            System.out.println("executing request "
                    + httppost.getRequestLine());
            HttpResponse response = httpclient.execute(httppost);
            StatusLine status = response.getStatusLine();
            System.out.println(status.toString());
        } catch (Exception e) {
            System.out.print(e.getMessage());
        } finally {
            httpclient.getConnectionManager().shutdown();
        }

    }
}
